Packer を Systems Manager オートメーションランブック AWS-RunPacker で実行してみた
コンバンハ、千葉(幸)です。
Packer に入門したての私がウロウロと AWS ドキュメントをうろついていると、AWS-RunPacker というイカした名称の Systems Manager オートメーションランブックが目に入ってきました。
見つけてしまったからには試さずにはいられないので、試してみました。
AWS-RunPacker で何ができるのか
Packer を AWS Systems Manager オートメーションによる一時的な実行基盤で実行させる事ができます。
ランブックの説明に入る前に Packer について簡単におさらいしておきましょう。( Packer 自体は様々な環境で使用できますが、ここでは AWS における AMI の作成に限定して言及します。)
Packer の用途は AMI の作成です。テンプレートであるべき姿を定義しておき、それをビルドする事で以下の流れで AMI の作成が実行されます。
- 一時的なインスタンスの作成
- インスタンスへの SSH 接続および定義された処理の実行
- 処理が完了したインスタンスから AMI の作成
- 一時的なリソースの削除
Packer の実行は手元の端末からリモートで実行することもできますし、例えば EC2 インスタンスにセットアップすればそこから実行することもできます。
「どこから実行するか」に加えて、テンプレートをどう管理するかや、実行に必要な IAM の認証情報をどう管理するかも考慮が必要な点となります。
AWS-RunPacker を使用することで、以下のイメージとなります。
- 実行環境:Systems Manager オートメーションによる一時的な実行環境が作成される
- テンプレート:S3 バケットに配置した Packer テンプレートが環境にダウンロードされる
- 認証情報:オートメーション用ロール(もしくは操作者)の認証情報が使用される
手元の端末から Packer を実行可能だしチームでなく一人で使っている、という環境ではあまりありがたみはなさそうですが、専用の実行サーバなどを用意して使っていた場合には AWS-RunPacker に乗り換えることでメリットは出そうです。
AWS-RunPacker の基本情報
インプットパラメーター
以下のパラメーターを指定できます。
パラメータ名 | 指定 | 概要 |
---|---|---|
TemplateS3BucketName | 必須 | Packer テンプレートが格納されたバケット名 |
TemplateFileName | 必須 | テンプレートのファイル名(パスを含む) |
Mode | 必須 | Build,Validate,Fixのいずれか。デフォルトはBuild |
Force | 必須 | Build のオプション。true か falseでデフォルトは true |
AutomationAssumeRole | オプション | オートメーション実行時に引き受けるロール |
いくつかさらに補足をしておきます。
Mode
Packer コマンドのいくつかをここで指定できます。
Build
:テンプレートからアーティファクト(ここでは AMI )を作成Validate
:テンプレートの構文が正しいかを確認Fix
:テンプレートの下位互換性の無い部分を見つけて最新バージョンの Packer で使用できるように修正
詳細は以下を参照してください。
Force
Force は build コマンドのオプションです。以前のビルドのアーティファクトにより実行が妨げられた場合に、強制的に続行する場合に true を指定します。
例えば作成予定の AMI と同じ名称の AMI が存在している場合 通常はビルドが失敗しますが、--force
オプションを付与することで既存の AMI の削除を伴う形でビルドが成功します。
アウトプットパラメーター
出力として以下が定義されています。
パラメータ名 | 概要 |
---|---|
RunPackerProcessTemplate.output | Packer ツールによる標準出力 |
RunPackerProcessTemplate.s3_bucket | Fix されたテンプレートの格納バケット |
RunPackerProcessTemplate.fixed_template_key | Fix されたテンプレートのファイル名(パスを含む) |
一点目は手元で Packer を実行した際に標準出力されるものと同じ内容です。ただし、オートメーションの処理が完了してからでないと出力されないことに注意が必要です。(手元で実行する際は進捗状況に応じて随時出力される。)
二点目と三点目は Mode に Fix を指定した場合のみ有効な出力です。
コンテンツ
2021/06/08現在のコンテンツの内容は以下の通りです。
ドキュメントの作成は 2021/05/28 となっていたので、ある程度定期的に内容は更新されているようです。使用する上ではコンテンツの中身をすべて理解する必要はないので、大まかな流れを抑えたい場合にご参照ください。
折り畳み
# # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # --- description: | ### Document Name - AWS-RunPacker ## What does this document do? This document uses the HashiCorp [packer](https://www.packer.io/) tool to validate, fix, or build packer templates that are used to create machine images. This document is using Packer v1.7.2. ## Input Parameters * TemplateS3BucketName: The name of the Amazon S3 bucket containing the packer template. * TemplateFileName: The name, or key, of the template file in the S3 bucket. * Mode: The mode, or command, in which to use Packer when validating against the template: * [Build](https://www.packer.io/docs/commands/build.html): (Default) Runs all the builds within the template in order to generate a set of artifacts. * [Validate](https://www.packer.io/docs/commands/validate.html): Validates the syntax and configuration of the template. * [Fix](https://www.packer.io/docs/commands/fix.html): Finds backwards incompatible content in the template and updates it for use with the latest version of Packer. It then uploads the fixed template to the S3 bucket that you specify. The name of the fixed template is identical to the template provided by the user, but with "fixed-" prepended to the name. * [Force](https://www.packer.io/docs/commands/build.html#force): Forces a builder to run when artifacts from a previous build otherwise prevent a build from running. * True: (Default) Force flag is used * False: Force flag is not used * AutomationAssumeRole: The ARN of the role that allows Automation to perform the actions on your behalf. ## Output Parameters * RunPackerProcessTemplate.output: The stdout from the Packer tool. * RunPackerProcessTemplate.fixed_template_key: The name of the template stored in an S3 bucket to use only when running in "Fix" mode. * RunPackerProcessTemplate.s3_bucket: The name of the S3 bucket that contains the fixed template to use only when running in "Fix" mode. schemaVersion: '0.3' assumeRole: "{{ AutomationAssumeRole }}" parameters: TemplateS3BucketName: type: String description: "(Required) AWS S3 bucket name that stores the template. eg. my-packer-bucket" TemplateFileName: type: String description: "(Required) Packer template file key in the bucket. eg. path/to/packer-template.json" Mode: type: String description: "(Required) The mode in which to use Packer when validating against the template. Default Value - Build" allowedValues: - Validate - Fix - Build default: 'Build' Force: type: String description: "(Optional) Forces a builder to run when artifacts from a previous build otherwise prevent a build from running. Default Value - True" allowedValues: - 'True' - 'False' default: 'True' AutomationAssumeRole: type: String description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf. default: '' outputs: - RunPackerProcessTemplate.output - RunPackerProcessTemplate.fixed_template_key - RunPackerProcessTemplate.s3_bucket mainSteps: - name: RunPackerProcessTemplate action: aws:executeScript timeoutSeconds: 600 description: | ## RunPackerProcessTemplate Runs the selected mode against the template using the Packer tool ## Outputs * output: The stdout from the packer tool * Fixed_template_key: The name of the fixed template in S3 if run in "Fix" mode. No value otherwise * s3_bucket: The name of your s3 that contains the fixed template if run in "Fix" mode. No value otherwise inputs: Runtime: python3.6 Handler: run_packer_handler Attachment: packer_1.7.2_linux_amd64.zip InputPayload: TemplateFileName: '{{TemplateFileName}}' TemplateS3BucketName: '{{TemplateS3BucketName}}' Mode: '{{Mode}}' Force: '{{Force}}' Script: | import os import glob import boto3 import subprocess from pathlib import Path import json def get_current_dir(): current_path = os.path.realpath(__file__) p = Path(current_path) return str(p.parent) def execute_packer_command(cmdArray): p = subprocess.Popen(cmdArray, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = [(x.decode("utf-8")) for x in p.communicate()] return_code = p.returncode if err != "": raise Exception("ERROR IN EXECUTING PACKER", err) if return_code != 0: raise Exception("FAILED", out) return out def download_file(path, key, bucket_name): s3 = boto3.client('s3') s3.download_file(bucket_name, key, path) def upload_file(path, key, bucket_name): s3 = boto3.client('s3') s3.upload_file(path, bucket_name, key) def validate_template(packer_cmd, template_path): validate_template_cmd = [packer_cmd, "validate", template_path] out = execute_packer_command(validate_template_cmd) print("Template validated successfully") return out def build_template(packer_cmd, template_path, force_build): build_command = [packer_cmd, "build"] if force_build == "True": build_command.extend(["-machine-readable", "-force"]) build_command.append(template_path) out = execute_packer_command(build_command) print("Template built") return out def fix_template(packer_cmd, local_template_path, s3_template_path, bucket_name): s3_path_head, template_file_name = os.path.split(s3_template_path) fixed_template_file_name = "fixed-" + template_file_name fixed_template_path = os.path.join('/tmp/', fixed_template_file_name) fixed_template_cmd = [packer_cmd, "fix", local_template_path] out = execute_packer_command(fixed_template_cmd) fixed_template_file = open(fixed_template_path, "w") fixed_template_file.write(out) fixed_template_file.close() fixed_s3_key = os.path.join(s3_path_head, fixed_template_file_name) upload_file(fixed_template_path, fixed_s3_key, bucket_name) print ("Template fixed from " + s3_template_path + " to " + fixed_s3_key) return out, fixed_s3_key def run_packer_handler(events, context): current_dir = get_current_dir() packer_cmd = current_dir + "/" + "packer" s3_template_path = events['TemplateFileName'] bucket_name = events['TemplateS3BucketName'] mode = events['Mode'] force_build = events['Force'] s3_path_head, file_name = os.path.split(s3_template_path) local_template_path = os.path.join("/tmp/", file_name) download_file(local_template_path, s3_template_path, bucket_name) os.system("chmod u=x " + packer_cmd) fixed_template_path = "" ret_bucket_value = "" if mode == "Validate": out = validate_template(packer_cmd, local_template_path) elif mode == "Build": out = build_template(packer_cmd, local_template_path, force_build) elif mode == "Fix": out, fixed_template_path = fix_template(packer_cmd, local_template_path, s3_template_path, bucket_name) ret_bucket_value = bucket_name return { 'output': json.dumps(out), 'fixed_template_key': fixed_template_path, 's3_bucket': ret_bucket_value } isEnd: true outputs: - Name: output Selector: $.Payload.output Type: String - Name: fixed_template_key Selector: $.Payload.fixed_template_key Type: String - Name: s3_bucket Selector: $.Payload.s3_bucket Type: String files: packer_1.7.2_linux_amd64.zip: checksums: sha256: 9429c3a6f80b406dbddb9b30a4e468aeac59ab6ae4d09618c8d70c4f4188442e size: 28738303 ...
アタッチメント
ドキュメントの詳細画面から添付ファイルを確認できます。 Packer のバージョンは現時点で最新の 1.7.2 が使用されるようです。
ここからダウンロードすることもできます。
自身のロールを使用して実行する
一通り確認が完了したので実際に動かしてみましょう。
事前準備
あらかじめ以下のテンプレートを用意しました。
{ "builders": [ { "type": "amazon-ebs", "region": "ap-northeast-1", "instance_type": "t3.micro", "ssh_username": "ec2-user", "source_ami_filter": { "filters": { "name": "amzn2-ami-hvm-*-x86_64-gp2" }, "owners": ["137112412989"], "most_recent": true }, "vpc_id": "vpc-0e4acafc38414468c", "subnet_id": "subnet-0caa45223899b4b73", "associate_public_ip_address": true, "ami_name": "AmazonLinux2-{{isotime | clean_resource_name}}", "tags": { "Base_AMI_ID": "{{ .SourceAMI }}", "Base_AMI_NAME": "{{ .SourceAMIName }}" } } ], "provisioners": [ { "type": "shell", "inline": [ "sudo yum -y install tree" ] } ] }
ざっくり以下の内容です。
- 最新の Amazon Linux2 の AMI をソース AMI とする
- 一時インスタンスの配置先として特定の VPC とサブネット(パブリック)を指定しパブリック IP アドレスの割り当てを許可する
- AMI 名の末尾に実行時間のタイムスタンプを付与し、ソース AMI の ID と 名称をタグとして付与する
- tree を yum インストールする
上記のテンプレートを S3 バケットchibayuki-template
のトップディレクトリ(という表現は正確ではありませんが)に格納してあります。
オートメーションの実行(コンソール)
マネジメントコンソールから実行していきます。なお、今回はAdministratorAccess
を持つ IAM ロールにスイッチして作業しています。
ドキュメントの詳細画面から「オートメーションの実行」を押下することで実行画面に遷移できますが、お手軽に以下の URL にアクセスします。
https://ap-northeast-1.console.aws.amazon.com/systems-manager/automation/execute/AWS-RunPacker
実行方法として「シンプルな実行」を選択し、各種パラメータを入力します。
パラメータ名 | 値 |
---|---|
TemplateS3BucketName | chibayuki-template |
TemplateFileName | amazonlinux2.json |
Mode | Build |
Force | True |
AutomationAssumeRole | なし |
画面下部で実行リンクと AWS CLI コマンドが生成できます。
前者の例は以下、
https://ap-northeast-1.console.aws.amazon.com/systems-manager/automation/execute/AWS-RunPacker?region=ap-northeast-1#TemplateS3BucketName=chibayuki-template&TemplateFileName=amazonlinux2.json&Mode=Build&Force=True&AutomationAssumeRole=&tagsOnCreate=%5B%5D
後者の例は以下です。(なお、AutomationAssumeRole
の値がブランクのままだと CLI は失敗するので、指定しない場合はキーごと削除する必要があります。)
aws ssm start-automation-execution --document-name "AWS-RunPacker" --document-version "\$DEFAULT" --parameters '{"TemplateS3BucketName":["chibayuki-template"],"TemplateFileName":["amazonlinux2.json"],"Mode":["Build"],"Force":["True"],"AutomationAssumeRole":[""]}' --region ap-northeast-1
コンソールから何度も実行したい場合は、パラメータをつど入力するのが手間なので実行リンクを控えておくといいでしょう。
最後に「実行」を押下すれば実行処理が開始されます。
以下のような実行詳細画面に遷移します。
AWS-RunPacker は単一のステップからなるドキュメントのため、ステップの進み具合を確認する、ということはできません。処理が終わるまでひたすら待ちましょう。
環境によりますが、短くても 3 分程度はかかるでしょう。完了するまで標準出力( Packer の実行ログ)が確認できないのは少しやきもきします。
5 分足らずで実行が完了しました。出力を確認してみましょう。
RunPackerProcessTemplate.output
として Packer の実行ログが確認できます。
せっかくなのでログを載せておきます。
折り畳み
"1623084960,,ui,say,==> amazon-ebs: Force Deregister flag found%!(PACKER_COMMA) skipping prevalidating AMI Name 1623084960,,ui,message, amazon-ebs: Found Image ID: ami-0ca38c7440de1749a 1623084960,,ui,say,==> amazon-ebs: Creating temporary keypair: packer_60be4f9f-20cd-34ea-3572-bf7056d76453 1623084960,,ui,say,==> amazon-ebs: Creating temporary security group for this instance: packer_60be4fa0-ea14-2e60-6593-168770925689 1623084960,,ui,say,==> amazon-ebs: Authorizing access to port 22 from [0.0.0.0/0] in the temporary security groups... 1623084961,,ui,say,==> amazon-ebs: Launching a source AWS instance... 1623084961,,ui,say,==> amazon-ebs: Adding tags to source instance 1623084961,,ui,message, amazon-ebs: Adding tag: \"Name\": \"Packer Builder\" 1623084962,,ui,message, amazon-ebs: Instance ID: i-002ca33d541534f99 1623084962,,ui,say,==> amazon-ebs: Waiting for instance (i-002ca33d541534f99) to become ready... 1623084977,,ui,say,==> amazon-ebs: Using ssh communicator to connect: 18.183.91.191 1623084977,,ui,say,==> amazon-ebs: Waiting for SSH to become available... 1623084990,,ui,say,==> amazon-ebs: Connected to SSH! 1623084990,,ui,say,==> amazon-ebs: Provisioning with shell script: /tmp/packer-shell059834347 1623084991,,ui,message, amazon-ebs: Loaded plugins: extras_suggestions%!(PACKER_COMMA) langpacks%!(PACKER_COMMA) priorities%!(PACKER_COMMA) update-motd 1623084991,,ui,error,==> amazon-ebs: Existing lock /var/run/yum.pid: another copy is running as pid 2398. 1623084991,,ui,error,==> amazon-ebs: Another app is currently holding the yum lock; waiting for it to exit... 1623084991,,ui,error,==> amazon-ebs: The other application is: yum 1623084991,,ui,error,==> amazon-ebs: Memory : 105 M RSS (418 MB VSZ) 1623084991,,ui,error,==> amazon-ebs: Started: Mon Jun 7 16:56:25 2021 - 00:06 ago 1623084991,,ui,error,==> amazon-ebs: State : Running%!(PACKER_COMMA) pid: 2398 1623084993,,ui,error,==> amazon-ebs: Existing lock /var/run/yum.pid: another copy is running as pid 2532. 1623084993,,ui,error,==> amazon-ebs: Another app is currently holding the yum lock; waiting for it to exit... 1623084993,,ui,error,==> amazon-ebs: The other application is: yum 1623084993,,ui,error,==> amazon-ebs: Memory : 53 M RSS (269 MB VSZ) 1623084993,,ui,error,==> amazon-ebs: Started: Mon Jun 7 16:56:31 2021 - 00:02 ago 1623084993,,ui,error,==> amazon-ebs: State : Running%!(PACKER_COMMA) pid: 2532 1623084995,,ui,error,==> amazon-ebs: Another app is currently holding the yum lock; waiting for it to exit... 1623084995,,ui,error,==> amazon-ebs: The other application is: yum 1623084995,,ui,error,==> amazon-ebs: Memory : 143 M RSS (360 MB VSZ) 1623084995,,ui,error,==> amazon-ebs: Started: Mon Jun 7 16:56:31 2021 - 00:04 ago 1623084995,,ui,error,==> amazon-ebs: State : Running%!(PACKER_COMMA) pid: 2532 1623084997,,ui,message, amazon-ebs: Resolving Dependencies 1623084997,,ui,message, amazon-ebs: --> Running transaction check 1623084997,,ui,message, amazon-ebs: ---> Package tree.x86_64 0:1.6.0-10.amzn2.0.1 will be installed 1623084997,,ui,message, amazon-ebs: --> Finished Dependency Resolution 1623084997,,ui,message, amazon-ebs: 1623084997,,ui,message, amazon-ebs: Dependencies Resolved 1623084997,,ui,message, amazon-ebs: 1623084997,,ui,message, amazon-ebs: ================================================================================ 1623084997,,ui,message, amazon-ebs: Package Arch Version Repository Size 1623084997,,ui,message, amazon-ebs: ================================================================================ 1623084997,,ui,message, amazon-ebs: Installing: 1623084997,,ui,message, amazon-ebs: tree x86_64 1.6.0-10.amzn2.0.1 amzn2-core 47 k 1623084997,,ui,message, amazon-ebs: 1623084997,,ui,message, amazon-ebs: Transaction Summary 1623084997,,ui,message, amazon-ebs: ================================================================================ 1623084997,,ui,message, amazon-ebs: Install 1 Package 1623084997,,ui,message, amazon-ebs: 1623084997,,ui,message, amazon-ebs: Total download size: 47 k 1623084997,,ui,message, amazon-ebs: Installed size: 83 k 1623084997,,ui,message, amazon-ebs: Downloading packages: 1623084997,,ui,message, amazon-ebs: Running transaction check 1623084997,,ui,message, amazon-ebs: Running transaction test 1623084997,,ui,message, amazon-ebs: Transaction test succeeded 1623084997,,ui,message, amazon-ebs: Running transaction 1623084997,,ui,message, amazon-ebs: Installing : tree-1.6.0-10.amzn2.0.1.x86_64 1/1 1623084997,,ui,message, amazon-ebs: Verifying : tree-1.6.0-10.amzn2.0.1.x86_64 1/1 1623084997,,ui,message, amazon-ebs: 1623084997,,ui,message, amazon-ebs: Installed: 1623084997,,ui,message, amazon-ebs: tree.x86_64 0:1.6.0-10.amzn2.0.1 1623084997,,ui,message, amazon-ebs: 1623084997,,ui,message, amazon-ebs: Complete! 1623084997,,ui,say,==> amazon-ebs: Stopping the source instance... 1623084997,,ui,message, amazon-ebs: Stopping instance 1623084998,,ui,say,==> amazon-ebs: Waiting for the instance to stop... 1623085013,,ui,say,==> amazon-ebs: Creating AMI AmazonLinux2-2021-06-07T16-55-59Z from instance i-002ca33d541534f99 1623085013,,ui,message, amazon-ebs: AMI: ami-0b50d7d2aafa48580 1623085013,,ui,say,==> amazon-ebs: Waiting for AMI to become ready... 1623085195,,ui,say,==> amazon-ebs: Adding tags to AMI (ami-0b50d7d2aafa48580)... 1623085195,,ui,say,==> amazon-ebs: Tagging snapshot: snap-0cec58bfdd01d9530 1623085195,,ui,say,==> amazon-ebs: Creating AMI tags 1623085195,,ui,message, amazon-ebs: Adding tag: \"Base_AMI_NAME\": \"amzn2-ami-hvm-2.0.20210427.0-x86_64-gp2\" 1623085195,,ui,message, amazon-ebs: Adding tag: \"Base_AMI_ID\": \"ami-0ca38c7440de1749a\" 1623085195,,ui,say,==> amazon-ebs: Creating snapshot tags 1623085195,,ui,say,==> amazon-ebs: Terminating the source AWS instance... 1623085210,,ui,say,==> amazon-ebs: Cleaning up any extra volumes... 1623085211,,ui,say,==> amazon-ebs: No volumes to clean up%!(PACKER_COMMA) skipping 1623085211,,ui,say,==> amazon-ebs: Deleting temporary security group... 1623085211,,ui,say,==> amazon-ebs: Deleting temporary keypair... 1623085211,,ui,say,Build 'amazon-ebs' finished after 4 minutes 11 seconds. 1623085211,,ui,say,\ ==> Wait completed after 4 minutes 11 seconds 1623085211,,ui,say,\ ==> Builds finished. The artifacts of successful builds are: 1623085211,amazon-ebs,artifact-count,1 1623085211,amazon-ebs,artifact,0,builder-id,mitchellh.amazonebs 1623085211,amazon-ebs,artifact,0,id,ap-northeast-1:ami-0b50d7d2aafa48580 1623085211,amazon-ebs,artifact,0,string,AMIs were created:\ ap-northeast-1: ami-0b50d7d2aafa48580\ 1623085211,amazon-ebs,artifact,0,files-count,0 1623085211,amazon-ebs,artifact,0,end 1623085211,,ui,say,--> amazon-ebs: AMIs were created:\ ap-northeast-1: ami-0b50d7d2aafa48580\ "
ともかく、 Packer による AMI 作成が正常に完了しました。
オートメーション用ロールを指定して実行する
先ほどは操作者の IAM 権限を使用して実行しましたが、オートメーション用ロールを作成し、そちらの権限を使用して実行するパターンを試してみます。
そうすることにより、操作者に与える権限を最小(オートメーションを実行できる)にしつつも、 Packer による AMI 作成などの操作を実現できます。
事前準備
オートメーション用ロール
以下ブログに記載の CloudFormation テンプレートを利用し作成しました。
折り畳み
AWSTemplateFormatVersion: "2010-09-09" Description: "Template to create sample IAM role to execute packer automation using SSM" Parameters: PackerTemplateS3BucketLocation: Type: String Description: Enter the name of the bucket where the packer templates will be stored. This is used to add permissions to the policy. For example, my-packer-bucket Resources: SSMAutomationPackerRole: Type: "AWS::IAM::Role" Properties: RoleName: "SSMAutomationPackerCF" ManagedPolicyArns: [ 'arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole' ] AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "sts:AssumeRole" Principal: Service: - "ec2.amazonaws.com" - "ssm.amazonaws.com" SSMAutomationPackerInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: InstanceProfileName: "SSMAutomationPackerCF" Roles: - !Ref SSMAutomationPackerRole SSMAutomationPackerInlinePolicy: Type: "AWS::IAM::Policy" Properties: PolicyName: "SSMAutomationPackerInline" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "iam:GetInstanceProfile" Resource: - "arn:aws:iam::*:instance-profile/*" - Effect: "Allow" Action: - "logs:CreateLogStream" - "logs:DescribeLogGroups" Resource: - "arn:aws:logs:*:*:log-group:*" - Effect: "Allow" Action: - "s3:ListBucket" Resource: - !Sub 'arn:aws:s3:::${PackerTemplateS3BucketLocation}' - Effect: "Allow" Action: - "s3:GetObject" Resource: - !Sub 'arn:aws:s3:::${PackerTemplateS3BucketLocation}/*' - Effect: "Allow" Action: - "ec2:DescribeInstances" - "ec2:CreateKeyPair" - "ec2:DescribeRegions" - "ec2:DescribeVolumes" - "ec2:DescribeSubnets" - "ec2:DeleteKeyPair" - "ec2:DescribeSecurityGroups" Resource: - "*" Roles: - !Ref SSMAutomationPackerRole SSMAutomationPackerPassrolePolicy: Type: "AWS::IAM::Policy" Properties: PolicyName: "SSMAutomationPackerPassrole" PolicyDocument: Version: "2012-10-17" Statement: - Sid: "SSMAutomationPackerPassrolePolicy" Effect: "Allow" Action: "iam:PassRole" Resource: !GetAtt SSMAutomationPackerRole.Arn Roles: - !Ref SSMAutomationPackerRole
これにより、以下ロールが作成さました。
- ロール名:
SSMAutomationPackerCF
- インスタンスプロファイル名:同上
- 信頼されたエンティティ:
ec2.amazonaws.com
,ssm.amazonaws.com
- アタッチされたポリシー
AmazonSSMAutomationRole
( AWS 管理ポリシー)SSMAutomationPackerInline
(インラインポリシー)SSMAutomationPackerPassrole
(インラインポリシー)
インラインポリシーの内訳はそれぞれ以下の通りです。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "ec2:AuthorizeSecurityGroupIngress", "ec2:DescribeInstances", "ec2:CreateKeyPair", "ec2:CreateSecurityGroup", "ec2:DeleteSecurityGroup", "ec2:DescribeRegions", "ec2:DescribeVolumes", "ec2:DescribeSubnets", "ec2:DeleteKeyPair", "ec2:DescribeSecurityGroups" ], "Resource": "*" }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:DescribeLogGroups", "iam:GetInstanceProfile", "s3:ListBucket" ], "Resource": [ "arn:aws:logs:*:*:log-group:*", "arn:aws:iam::*:instance-profile/*", "arn:aws:s3:::chibayuki-template" ] }, { "Sid": "VisualEditor2", "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::chibayuki-template/*" } ] }
↑ 今回使用する Packer テンプレートを前提にするとハイライト部の SecuriryGroup に関する操作権限が不足していることに気づいたので、手動で追記しました。
{ "Version": "2012-10-17", "Statement": [ { "Action": "iam:PassRole", "Resource": "arn:aws:iam::012345678910:role/SSMAutomationPackerCF", "Effect": "Allow", "Sid": "SSMAutomationPackerPassrolePolicy" } ] }
操作者用の IAM ポリシー
以下の IAM ポリシーを割り当てた IAM ユーザーで後続の作業を行います。
AmazonSSMFullAccess
(AWS管理ポリシー)- PassRole 用ポリシー(インラインポリシー)
インラインポリシーはオートメーション用ロールに付与したものと同一です。
{ "Version": "2012-10-17", "Statement": [ { "Action": "iam:PassRole", "Resource": "arn:aws:iam::012345678910:role/SSMAutomationPackerCF", "Effect": "Allow", "Sid": "SSMAutomationPackerPassrolePolicy" } ] }
これらの権限設定は以下ドキュメントを参考にしました。
SSM フルアクセスから更に絞れないかなと考えたのですが、沼にハマりそうなのでやめておきました。
オートメーションの実行( AWS CLI )
先ほどのコンソールから生成したコマンドを加工して、以下を作成しました。
aws ssm start-automation-execution\ --document-name "AWS-RunPacker"\ --document-version "\$DEFAULT"\ --parameters '{"TemplateS3BucketName":["chibayuki-template"],"TemplateFileName":["amazonlinux2.json"],"Mode":["Build"],"Force":["True"],"AutomationAssumeRole":["arn:aws:iam::012345678910:role/SSMAutomationPackerCF"]}'\ --region ap-northeast-1
テンプレートは前回と同じものを指定しています。コマンドを実行すると、実行 ID が返却されます。
% aws ssm start-automation-execution\ --document-name "AWS-RunPacker"\ --document-version "\$DEFAULT"\ --parameters '{"TemplateS3BucketName":["chibayuki-template"],"TemplateFileName":["amazonlinux2.json"],"Mode":["Build"],"Force":["True"],"AutomationAssumeRole":["arn:aws:iam::012345678910:role/SSMAutomationPackerCF"]}'\ --region ap-northeast-1 { "AutomationExecutionId": "162c4e17-de8d-4af6-a3b2-7b0dd676d25b" }
ここから更に CLI を駆使して色々参照したいところですが、「ちょっとめんどくさいな……」という思いが勝ったので、以降はコンソールから確認していきます。
戻り値として返ってきたオートメーション実行 ID が進行中であることが確認できます。
こちらも数分後に成功ステータスに遷移しました。これよりあとはコンソールからの実行と差異がないので割愛します。
余談:オートメーションの実行環境が使用するグローバル IP は何?
今回はテンポラリなインスタンスが使用する SecuriryGroup をテンプレートで定義していないため、一時的な SecuriryGroup が作成されます。ここでは 0.0.0.0/0 からの 22 ポートのインバウンドが許可されています。
インスタンスも含めて一時的なものであることからリスクは低いと判断しそのままにしましたが、送信元を絞りたい場合もあるかと思います。
では許可する送信元として何を指定すればいいのか?ということになりますが、実測できた範囲では以下の IP が使用されていました。
- 3.112.59.252
- 3.112.110.150
- 3.113.18.247
- 3.113.181.141
- 3.115.18.230
- 13.230.73.125
- 13.231.159.156
- 18.181.201.35
- 18.183.46.53
- 18.183.122.218
- 18.183.136.15
- 35.73.127.132
- 54.249.3.81
これらは Packer テンプレート内でsudo netstat -anp | grep ESTABLISHED
の処理を追加し、実行ログから接続元の グローバル IP アドレスを確認しました。該当箇所のログを抽出して並べると以下の通りです。
1623072997,,ui,message, amazon-ebs: tcp 0 0 192.168.0.174:22 3.113.18.247:35368 ESTABLISHED 2462/sshd: ec2-user 1623084997,,ui,message, amazon-ebs: tcp 0 0 192.168.0.152:22 3.113.181.141:60238 ESTABLISHED 2424/sshd: ec2-user 1623141688,,ui,message, amazon-ebs: tcp 0 0 192.168.0.233:22 3.112.59.252:58288 ESTABLISHED 2438/sshd: ec2-user 1623142506,,ui,message, amazon-ebs: tcp 0 0 192.168.0.245:22 18.183.136.15:33272 ESTABLISHED 2453/sshd: ec2-user 1623142535,,ui,message, amazon-ebs: tcp 0 0 192.168.0.104:22 18.183.46.53:41644 ESTABLISHED 2424/sshd: ec2-user 1623142548,,ui,message, amazon-ebs: tcp 0 0 192.168.0.109:22 13.231.159.156:46420 ESTABLISHED 2501/sshd: ec2-user 1623142560,,ui,message, amazon-ebs: tcp 0 0 192.168.0.243:22 18.181.201.35:53014 ESTABLISHED 2437/sshd: ec2-user 1623142524,,ui,message, amazon-ebs: tcp 0 0 192.168.0.168:22 3.112.110.150:36682 ESTABLISHED 2441/sshd: ec2-user 1623142657,,ui,message, amazon-ebs: tcp 0 0 192.168.0.117:22 35.73.127.132:37828 ESTABLISHED 2409/sshd: ec2-user 1623142999,,ui,message, amazon-ebs: tcp 0 0 192.168.0.99:22 13.230.73.125:58012 ESTABLISHED 2458/sshd: ec2-user 1623143221,,ui,message, amazon-ebs: tcp 0 0 192.168.0.163:22 3.115.18.230:54616 ESTABLISHED 2402/sshd: ec2-user 1623143357,,ui,message, amazon-ebs: tcp 0 36 192.168.0.46:22 54.249.3.81:55662 ESTABLISHED 2430/sshd: ec2-user 1623143713,,ui,message, amazon-ebs: tcp 0 0 192.168.0.197:22 18.183.122.218:51354 ESTABLISHED 2449/sshd: ec2-user
これらの IP アドレスは、以下から「東京リージョンのEC2
サービス」が使用する IP レンジに含まれているところまで確認できました。
% jq -r '.prefixes[] | select(.region=="ap-northeast-1" and .service=="EC2") | .ip_prefix' < ip-ranges.json | sort -n 3.112.0.0/14 3.5.152.0/21 13.112.0.0/14 13.230.0.0/15 15.177.79.0/24 15.193.1.0/24 18.176.0.0/15 18.178.0.0/16 18.179.0.0/16 18.180.0.0/15 18.182.0.0/16 18.183.0.0/16 35.72.0.0/13 46.51.224.0/19 52.192.0.0/15 52.194.0.0/15 52.196.0.0/14 52.68.0.0/15 52.94.248.80/28 52.95.243.0/24 52.95.255.48/28 54.150.0.0/16 54.168.0.0/16 54.178.0.0/16 54.199.0.0/16 54.238.0.0/16 54.248.0.0/15 54.250.0.0/16 54.64.0.0/15 54.92.0.0/17 54.95.0.0/16 64.252.110.0/24 64.252.111.0/24 64.252.112.0/24 64.252.113.0/24 99.150.48.0/21 99.77.139.0/24 99.77.160.0/24 103.4.8.0/21 175.41.192.0/18 176.32.64.0/19 176.34.0.0/19 176.34.32.0/19
ここから更に絞れるかは確認する手段が思いつかなかったので、もし送信元 IP 制限をかけるなら上記をすべて使用することになりそうです。ちょっと厳しいですね。テンポラリな SecuriryGroup を許容する方が楽ではありそうです。
終わりに
AWS-RunPacker を試してみました。
操作者のロールを使用する場合でもオートメーション用ロールを使用する場合でも、Packer テンプレート内では特に認証情報に関する定義を入れなくても済む事が少し意外でした。
以下ブログではiam_instance_profile
としてオートメーション用ロールを指定していましたが、今回のような使い方であれば不要なようです。
何度か試してみて、 Packer によるビルドの進行状況が一通りオートメーションの実行が終わってからでないと確認できない(アウトプットに出力されない)というのが個人的には少し辛みがありました。
検証環境などで一人で気楽にバチバチ叩ける環境では従来通り手元の端末から実行して、お作法にのっとる必要がある場合には使用を検討する、というのが良さそうです。
手段の一つとして覚えておいてはいかがでしょうか。
以上、 チバユキ (@batchicchi) がお送りしました。